package org.twnmayberry.it;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import java.io.InputStream;
import java.util.Properties;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import junit.framework.Assert;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.twnmayberry.dto.POIDTO;
import org.twnmayberry.ejb.MayberryTestUtilRemote;
import org.twnmayberry.jms.MayberryPublisher;
import org.twnmayberry.jms.MayberrySubscriber;

/**
 * This integration test verifies the jmsNotifier functionality the gets wrapped
 * by the Ant scripts. It runs as an integration test with the JMS server
 * launched separately from mavn during the pre-integration phase.
 */
public class MayberryJMSIT {
	static final Log log = LogFactory.getLog(MayberryJMSIT.class);
	protected static String adminUser = System.getProperty("admin.user",
			"admin1");
	protected static String adminPassword = System.getProperty(
			"admin.password", "password");
	private static Context jndi;

	protected static Properties props;
	public static String topicName;
	public static String topicJNDI;
	public static String connFactoryJNDI;
	private MayberryTestUtilRemote mayTestUtil;

	private static final String mayTestUtilJNDI = System
			.getProperty("jndi.name.mayberryTestUtil",
					"twnMayberryWAR/MayberryTestUtilEJB!org.twnmayberry.ejb.MayberryTestUtilRemote");

	/** Creates the topic for use in tests */
	@BeforeClass
	public static void setUpClass() throws Exception {
		log.info("*** setUpClass() ***");

		// read property file used by the Ant script to use same properties
		InputStream in = Thread.currentThread().getContextClassLoader()
				.getResourceAsStream("mayberryJMS.properties");
		assertNotNull("could not locate properties file", in);
		props = new Properties();
		props.load(in);
		in.close();
		connFactoryJNDI = props.getProperty("jndi.name.connFactory");
		topicName = props.getProperty("jmx.name.testTopic");
		topicJNDI = props.getProperty("jndi.name.testTopic");

		// perform some setup on the topics used for the test
		log.debug("getting jndi initial context");
		jndi = new InitialContext();
		log.debug("jndi=" + jndi.getEnvironment());

	}

	private Context runAs(String username, String password)
			throws NamingException {
		if (jndi != null) {
			jndi.close();
		}
		Properties env = new Properties();
		if (username != null) {
			env.put(Context.SECURITY_PRINCIPAL, username);
			env.put(Context.SECURITY_CREDENTIALS, password);
		}
		log.debug(String.format("%s env=%s", username == null ? "anonymous"
				: username, env));
		System.out.println(String.format("%s env=%s",
				username == null ? "anonymous" : username, env));
		jndi = new InitialContext(env);

		return jndi;
	}

	@Before
	public void setUp() throws Exception {
		mayTestUtil = (MayberryTestUtilRemote) jndi.lookup(mayTestUtilJNDI);
		jndi = runAs(adminUser, "password");

	}

	@AfterClass
	public static void tearDownClass() throws Exception {
		if (jndi != null) {
			jndi.close();
		}
	}

	/**
	 * This test executes the demo publisher and subscriber classes in a test
	 * scenario.
	 */
	@Test
	public void publishToSubscribers() {
		log.info("*** publishToSubscribers ***");
		// Remove all POIs which could potentially be sent by the timer service.
		mayTestUtil.resetAll();
		String sleep0 = props.getProperty("subscriber.sleep");
		String durable0 = props.getProperty("subscriber.durable");
		String selector0 = props.getProperty("subscriber.selector");
		String subscriberUsername = props.getProperty("subscriber.username");
		String subscriberPassword = props.getProperty("subscriber.password");
		final MayberrySubscriber subscriber = new MayberrySubscriber(
				"subscriber0");
		try {
			subscriber.setConnFactory((ConnectionFactory) jndi
					.lookup(connFactoryJNDI));
			subscriber.setDestination((Destination) jndi.lookup(topicJNDI));
		} catch (NamingException e1) {
			log.error("Error looking up topic: " + e1);
			Assert.fail("Failed on naming error");
		}
		subscriber.setSleepTime(Long.parseLong(sleep0));
		subscriber.setUsername(subscriberUsername);
		subscriber.setPassword(subscriberPassword);
		subscriber.setSelector(selector0);
		subscriber.setDurable(Boolean.parseBoolean(durable0));
		subscriber.setMaxCount(2);
		assertEquals(subscriber.getCount(), 0);
		new Thread() {
			public void run() {
				try {
					subscriber.receive();
				} catch (Exception e) {
					log.error("Error receiving message: " + e);
					Assert.fail("Fail on receiving message");
				}
			}
		}.start();

		String sleep = props.getProperty("publisher.sleep");
		MayberryPublisher publisher = new MayberryPublisher("publisher0");
		publisher.setSleepTime(Long.parseLong(sleep));
		POIDTO badDude = new POIDTO();
		badDude.setFirstName("Bad");
		badDude.setMiddleName("Danger");
		badDude.setLastName("Dude");
		assertEquals(publisher.getCount(), 0);

		try {
			ConnectionFactory connFactory = (ConnectionFactory) jndi
					.lookup(connFactoryJNDI);
			Connection connection = connFactory.createConnection("sysMayberry",
					"password");
			Session session = connection.createSession(false,
					Session.AUTO_ACKNOWLEDGE);
			Destination dest = (Destination) jndi.lookup(topicJNDI);
			publisher.publishPOI(session, dest, badDude, true);
			assertEquals(publisher.getCount(), 1);
			assertEquals(subscriber.getCount(), 1);
			publisher.publishPOI(session, dest, badDude, false);
			assertEquals(publisher.getCount(), 2);
			assertEquals(subscriber.getCount(), 1);
			publisher.publishPOI(session, dest, badDude, true);
			assertEquals(publisher.getCount(), 3);
			assertEquals(subscriber.getCount(), 2);
		} catch (JMSException e1) {
			log.error("Error sending JMS message: " + e1);
		} catch (Exception e) {
			log.error("Error in JMS test: " + e);
		}

	}
}